﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using aboveXWords.Properties;

namespace aboveXWords.GUI
{
  /// <summary>
  /// A square with a question, not actually a letter.
  /// </summary>
  [Serializable]
  public class Question : ResizableSquare
  {
    public Question(Square square)
      : base(square)
    {
      Value = "?";
    }

    public string Value { get; set; }

    public bool Fit { get; set; }

    /// <summary>
    /// Adjust the question to fit in the square.
    /// </summary>
    /// <param name="graphics"></param>
    protected internal override void Paint(Graphics graphics)
    {
      // Set string format.
      StringFormat newStringFormat = StringFormat.GenericTypographic;
      newStringFormat.FormatFlags = StringFormatFlags.FitBlackBox;
      RectangleF rectangle = Rectangle;
      rectangle.Inflate(-1, -1);

      // Measure string.
      Fit = false;
      Font stringFont = Settings.Default.QuestionFont;
      string measureString = String.Empty;
      for (int fontSize = 16; fontSize > 4; --fontSize)
      {
        int linesExpected = 1;
        measureString = String.Empty;
        stringFont = new Font(stringFont.FontFamily, fontSize);
        foreach (string word in Split(Value.ToUpperInvariant()))
        {
          bool firstWord = String.IsNullOrEmpty(measureString);
          bool endsWithHyphen = firstWord ? false : measureString.Last() == '-';
          int charactersFitted;
          int linesFilled;
          string nextWord = endsWithHyphen ? word : " " + word;
          string stringSoFar = firstWord ? word : measureString + nextWord;
          graphics.MeasureString(stringSoFar,
                                                stringFont, rectangle.Size, newStringFormat, out charactersFitted,
                                                out linesFilled);

          // If resulting rectangle to wide, insert linefeed and try again unless,
          // this is the first word, then we fail.
          if (linesFilled != linesExpected || charactersFitted != stringSoFar.Length)
          {
            if (!firstWord)
            {
              ++linesExpected;
              measureString += Environment.NewLine + word;
            }
            else
              measureString = stringSoFar;
          }
          else
            measureString = stringSoFar;
          SizeF resultingSize = graphics.MeasureString(measureString, stringFont,
                                                                      new SizeF(10000, 10000), newStringFormat,
                                                                      out charactersFitted,
                                                                      out linesFilled);
          Fit = rectangle.Width >= resultingSize.Width && rectangle.Height >= resultingSize.Height;
        }
        if (Fit) break;
      }
      // Draw string to screen.
      graphics.FillRectangle(Painter.Colors.LetterBackgroundBrush, Rectangle);
      base.Paint(graphics);
      newStringFormat.Alignment = StringAlignment.Center;
      newStringFormat.LineAlignment = StringAlignment.Center;
      graphics.DrawString(measureString, stringFont,
                                         Fit ? Painter.Colors.QuestionBrush : Painter.Colors.QuestionNotFitBrush,
                                         rectangle, newStringFormat);
    }

    /// <summary>
    /// Split string into words separated by space or hyphen. Multiple
    /// spaces are conserved.
    /// </summary>
    /// <param name="question">The string to split.</param>
    /// <returns>The splitted string</returns>
    internal static IEnumerable<string> Split(IEnumerable<char> question)
    {
      var words = new List<string>();
      string word = string.Empty;
      bool findEnd = false;
      foreach (char s in question)
      {
        if (findEnd)
        {
          if (s == ' ' || s == '-')
          {
            if (s == '-')
              word += s;
            words.Add(word);
            word = String.Empty;
            findEnd = false;
            continue;
          }
        }
        else
          findEnd = s != ' ';
        word += s;
      }
      words.Add(word);
      return words.ToArray();
    }

    public override bool DropOk(Square square)
    {
      return !(square is ResizableSquare);
    }

    public void Edit(Canvas canvas)
    {
      new QuestionDialog(canvas, this).ShowDialog();
    }
  }
}